package sep.einfuehrung;

import static com.googlecode.javacv.cpp.opencv_core.IPL_DEPTH_8U;
import static com.googlecode.javacv.cpp.opencv_core.cvCreateImage;
import static com.googlecode.javacv.cpp.opencv_core.cvGetSize;
import static com.googlecode.javacv.cpp.opencv_imgproc.CV_BGR2GRAY;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvCanny;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvCvtColor;

import javax.swing.event.EventListenerList;

import com.googlecode.javacv.FrameGrabber;
import com.googlecode.javacv.FrameGrabber.Exception;
import com.googlecode.javacv.cpp.opencv_core.IplImage;

/**
 * Playback and filter thread for the video
 */
public class VideoplayerThread extends Thread {

	private volatile boolean mRun;
	private volatile double mThreshold1;
	private volatile double mThreshold2;
	private volatile int mAperatureSize;
	private volatile FrameGrabber mGrabber;
	private volatile PlaybackState mState;
	private VideoPanel mVideopanel;
	private EventListenerList mListeners = new EventListenerList();

	/**
	 * The current playback state of the video player
	 */
	public enum PlaybackState {
		Playing, Paused, Stopped, NoFile;
	}

	/**
	 * Creates the playback thread
	 * 
	 * @param panel
	 *            Panel used to display the video
	 * @param grabber
	 *            Grabber for the video file
	 */
	public VideoplayerThread(VideoPanel panel, FrameGrabber grabber) {
		if (grabber == null || panel == null)
			throw new IllegalArgumentException("Params must not be null.");

		mVideopanel = panel;
		mGrabber = grabber;
	}

	/**
	 * Plays the video and filters it with canny edge filter
	 */
	@Override
	public void run() {
		mRun = true;
		mState = PlaybackState.Stopped;

		IplImage img = null;
		IplImage canny = null;

		while (mRun) {
			if (mState == PlaybackState.Playing) {

				try {
					img = mGrabber.grab();
				} catch (Exception e) {
					mRun = false;
					break;
				}

				if (img != null) {
					// Create grayscale image only once
					// (image size is equal for the whole video)
					if (canny == null)
						canny = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);

					// Convert grabbed image to grayscale
					cvCvtColor(img, canny, CV_BGR2GRAY);

					// Filter the grayscale image with canny edge filter and
					// show it
					cvCanny(canny, canny, mThreshold1, mThreshold2,
							mAperatureSize);
					
					if (mRun) {
						mVideopanel.setImages(canny.getBufferedImage(),
								img.getBufferedImage());
					}
				} else { // Video is at the end
					stopPlayback();
					notifyListeners();
				}
			} else {
				// Sleep 1ms to reduce cpu load
				try {
					sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		stopPlayback();
		
		try {
			mGrabber.stop();
		} catch (Exception e) {
		}
		
		mGrabber = null;
	}

	/**
	 * Quit the thread
	 */
	public void cancel() {
		mRun = false;
	}

	/**
	 * Start/resume playback
	 */
	public void play() {
		if (mState == PlaybackState.Stopped) {
			// Start playback from the beginning
			try {
				mGrabber.restart();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		mState = PlaybackState.Playing;
	}

	/**
	 * Pause playback
	 */
	public void pause() {
		mState = PlaybackState.Paused;
	}

	/**
	 * Stop playback
	 */
	public void stopPlayback() {
		mState = PlaybackState.Stopped;
	}

	/**
	 * Set the threshold1 param for the canny edge filter
	 * 
	 * @param val
	 *            threshold1 param
	 */
	public void setThreshold1(double val) {
		mThreshold1 = val;
	}

	/**
	 * Set the threshold2 param for the canny edge filter
	 * 
	 * @param val
	 *            threshold1 param
	 */
	public void setThreshold2(double val) {
		mThreshold2 = val;
	}

	/**
	 * Set the aperature size param for the canny edge filter
	 * 
	 * @param val
	 *            aperature size param
	 */
	public void setAperatureSize(int val) {
		mAperatureSize = val;
	}

	/**
	 * Register a listener
	 * 
	 * @param listener
	 *            Listener class
	 */
	public void addListener(VideoplayerListener listener) {
		mListeners.add(VideoplayerListener.class, listener);
	}

	private void notifyListeners() {
		for (VideoplayerListener l : mListeners
				.getListeners(VideoplayerListener.class)) {
			l.videoStopped();
		}
	}
}
